//
//  YuanXinWebView+WkWebView.m
//  YuanXinWebView
//
//  Created by 晏德智 on 16/7/28.
//  Copyright © 2016年 晏德智. All rights reserved.
//

#import "YuanXinWKWebView.h"
#import <WebKit/WebKit.h>
#import <UIKit/UIKit.h>
#import "CustomWKWebView.h"
#import "JWCacheURLProtocol.h"
#import "NSURLProtocol+WKWebVIew.h"
#import "YuanXinURLProtocol.h"
#import "UIDevice+SwitchOrientation.h"

#define SYSTEM_VERSION_GRATERTHAN_OR_EQUALTO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define MAX_RELOAD 3
#define kSCREEN_WIDTH ([UIScreen mainScreen].bounds.size.width)
#define kSCREEN_HEIGHT ([UIScreen mainScreen].bounds.size.height)

@interface CustomLongPressGestureRecognizer : UILongPressGestureRecognizer

@end

@implementation CustomLongPressGestureRecognizer

@end

@interface WeakScriptMessageDelegate : NSObject <WKScriptMessageHandler>

@property (weak, nonatomic) id<WKScriptMessageHandler> delegate;

@end

@implementation WeakScriptMessageDelegate

- (instancetype)init
{
    self = [super init];
    if (self) {
        //
    }
    return self;
}

- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)delegate {
    self = [super init];
    if (self) {
        //
        self.delegate = delegate;
    }
    return self;
}

#pragma mark -- WKScriptMessageHandler methods
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    if ([self.delegate respondsToSelector:@selector(userContentController:didReceiveScriptMessage:)]) {
        [self.delegate userContentController:userContentController didReceiveScriptMessage:message];
    }
}

@end


@interface YuanXinWKWebView()<WKNavigationDelegate,WKUIDelegate,WKScriptMessageHandler,UIGestureRecognizerDelegate,NSURLSessionDataDelegate,UIScrollViewDelegate>

@property (strong, nonatomic) CALayer *progressLayer;
@property (copy, nonatomic) UIColor *progressColor;
@property (assign, nonatomic) CGFloat progressHeight;
@property (strong, nonatomic) NSURLSession *sessionClient;
@property (copy, nonatomic) WKWebViewConfiguration *webViewConfig;
@property (assign, nonatomic) BOOL videoFullScreen;
@property (strong, nonatomic) CustomWKWebView *fakeWKWebView;
@property (strong, nonatomic) NSMutableArray<NSHTTPCookie *> *wkCookies;

@end

@implementation YuanXinWKWebView
{
    CustomWKWebView *_webView;
    BOOL _authed;
    NSInteger _reloadCount;
//    NSMutableArray<NSHTTPCookie *> * _wkCookies;
    UIView *_progressView;
    NSDictionary<NSString *, NSString *> *_sourceHeaders;
    id<UIScrollViewDelegate> _oldScrollDelegate;
    CGPoint _oldOffset;
}

@synthesize injectedJavaScript   =    _injectedJavaScript;

@synthesize onLoadingStart       =    _onLoadingStart;
@synthesize onLoadingFinish      =    _onLoadingFinish;
@synthesize onLoadingError       =    _onLoadingError;
@synthesize onShouldStartLoadWithRequest = _onShouldStartLoadWithRequest;
@synthesize onProgress           =    _onProgress;
@synthesize onBridgeMessage      =    _onBridgeMessage;

@synthesize automaticallyAdjustContentInsets = _automaticallyAdjustContentInsets;
@synthesize contentInset         =   _contentInset;
@synthesize sendCookies          =   _sendCookies;
@synthesize assetsCacheEnable    =   _assetsCacheEnable;
@synthesize noTouchCallout       =   _noTouchCallout;
@synthesize iosKeyboardDisableAutoScroll = _iosKeyboardDisableAutoScroll;
@synthesize source               =   _source;
@synthesize progressStyle        =   _progressStyle;
@synthesize showProgress         =   _showProgress;
@synthesize userName             =   _userName;
@synthesize password             =   _password;
@synthesize useURLProtocolForWKWebview = _useURLProtocolForWKWebview;
@synthesize userAgent            =   _userAgent;

#pragma mark lazy load
- (NSURLSession *)sessionClient {
    if (!_sessionClient) {
        // 初始化一个请求类专门负责预请求
        NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
        NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
        _sessionClient = session;
    }

    return _sessionClient;
}

- (WKWebViewConfiguration *)webViewConfig {
    if (!_webViewConfig) {
        WKWebViewConfiguration* configuration = [[WKWebViewConfiguration alloc] init];
        WKUserContentController *contentController = [[WKUserContentController alloc] init];
        [contentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"YuanXin"];
        // 注入 bridgeScript (大写的 window.WebViewBridge)
        NSString *yuanXinBridge = self.bridgeScript;
        WKUserScript * bridgeScript = [[WKUserScript alloc]
                                       initWithSource:yuanXinBridge injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:false];
        [contentController addUserScript:bridgeScript];
        // 先默认注入一个小写的 window.webViewBridge
        [self addUserScriptInjectedDefaultToUserContentController:contentController];
        // 注入 injectedJavaScript
        if (self.injectedJavaScript) {
            WKUserScript * bridgeScript = [[WKUserScript alloc]
                                           initWithSource:self.injectedJavaScript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:false];
            [contentController addUserScript:bridgeScript];
        }
        // 注入AjaxHandler
        [self addUserScriptAjaxHandlerToUserContentController:contentController];

        /*
         WKUserScript * cookieScript = [[WKUserScript alloc]
         initWithSource: @"document.cookie = 'TeskCookieKey1=TeskCookieValue1';document.cookie = 'TeskCookieKey2=TeskCookieValue2';"
         injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];

         [contentController addUserScript:cookieScript];
         */
        configuration.userContentController = contentController;
        //允许视频播放
        if (SYSTEM_VERSION_GRATERTHAN_OR_EQUALTO(@"9.0")) {
            configuration.allowsAirPlayForMediaPlayback = YES;
        } else {
            configuration.mediaPlaybackAllowsAirPlay = YES;
        }
        // 用来控制网页中的视频是内联播放(YES)还是全屏播放(NO)，默认是NO。
        configuration.allowsInlineMediaPlayback = YES;
        // 允许可以与网页交互，选择视图
        configuration.selectionGranularity = YES;
        // web内容处理池
        configuration.processPool = [[WKProcessPool alloc] init];

        /*
         // Webview的偏好设置
         WKPreferences *wkpreferences = [WKPreferences new];
         [wkpreferences setJavaScriptEnabled:true];
         [wkpreferences setMinimumFontSize:10];
         // 默认是不能通过JS自动打开窗口的，必须通过用户交互才能打开
         [wkpreferences setJavaScriptCanOpenWindowsAutomatically:YES];
         configuration.preferences = wkpreferences;
         */

        _webViewConfig = configuration;
    }

    return _webViewConfig;
}

#pragma mark - Initialization

- (void)dealloc {
    [self setOrientationPortrait];
    [_webView.configuration.userContentController removeScriptMessageHandlerForName:@"YuanXin"];
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    [JWCacheURLProtocol cancelListeningNetWorking];//在不需要用到webview的时候及时的取消监听
    // NSURLProtocol+WKWebVIew
    [NSURLProtocol wk_unregisterScheme:@"http"];
    [NSURLProtocol wk_unregisterScheme:@"https"];
    //
    [YuanXinURLProtocol cancelListeningNetWorking];
    NSLog(@"dealloc");

    [self invalidWebView];
}

- (instancetype)initWithFrame:(CGRect)frame
{
    if ((self = [super initWithFrame:frame])) {
        // TODO:todo->gjs:临时方案，先存储一下 Cookie，避免被清，原因不明。。
        // 可能 [WKWebsiteDataStore defaultDataStore].httpCookieStore 是只读属性(readonly)，它应该是根据 WKWebView 请求网页自动设置的，所以，如果是手动设置的，它不定在什么时候赋值时就没了。
        // 创建一个 WKWebView 后，[WKWebsiteDataStore defaultDataStore].httpCookieStore getAllCookies 里面手动设置的 cookie 就没了。。
        // 所以这里临时存储一下
        __weak typeof (self) weakSelf = self;
        if (@available(iOS 11.0, *)) {
            [[WKWebsiteDataStore defaultDataStore].httpCookieStore getAllCookies:^(NSArray<NSHTTPCookie *> * arrayCookies) {
                weakSelf.wkCookies = [arrayCookies mutableCopy];
                //NSLog(@"cookies in [WKWebsiteDataStore defaultDataStore].httpCookieStore:\n%@", arrayCookies);
            }];
        } else {
            // Fallback on earlier versions
        }

//        [[NSNotificationCenter defaultCenter] addObserver:self
//                                                 selector:@selector(keyboardWasShown:)
//                                                     name:UIKeyboardDidShowNotification object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(keyboardWillHide:)
                                                      name:UIKeyboardWillHideNotification object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                  selector:@selector(keyboardWillShown:)
                                                      name:UIKeyboardWillShowNotification object:nil];

        // 监听全屏
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                         selector:@selector(videoBeginFullScreen)
                                                             name:UIWindowDidBecomeVisibleNotification
                                                           object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                         selector:@selector(videoStopFullScreen)
                                                             name:UIWindowDidBecomeHiddenNotification
                                                           object:nil];
        // 监听屏幕旋转
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(handleDeviceOrientationDidChange)
                                                     name:UIDeviceOrientationDidChangeNotification
                                                   object:nil];
    }
    return self;
}

- (void)invalidWebView {
    if (_sessionClient) {
        [_sessionClient invalidateAndCancel];
        _sessionClient = nil;
    }
    if (_webView) {
        _webView.UIDelegate = nil;
        _webView.navigationDelegate = nil;
        [_webView stopLoading];
        [_webView removeFromSuperview];
        @try {
            [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
        }
        @catch (NSException * __unused exception) {}
        _webView = nil;
    }
    _webViewConfig = nil;
}

//重置 webview
- (void)setupWebView {
    if (_webView) {
        [self invalidWebView];
    }
    if (!_webView) {
        __weak typeof (self) weakSelf = self;
        _webView = [[CustomWKWebView alloc] initWithFrame:self.bounds configuration:self.webViewConfig];
        [self addSubview:_webView];
        _webView.navigationDelegate = weakSelf;
        _webView.UIDelegate = weakSelf;
        _webView.noTouchCallout = self.noTouchCallout;
        // 添加属性监听
        [_webView addObserver:weakSelf forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionNew context:nil];
    }
    [self setupProgressView];
}

- (void)setupProgressView {
    //进度条
    if (_progressView) {
        [_progressView removeFromSuperview];
    } else {
        _progressView = [[UIView alloc] init];
    }

    self.progressColor = self.progressColor ?: [UIColor blueColor];
    self.progressHeight = self.progressHeight > 0 ? self.progressHeight : 2;

    _progressView.frame = CGRectMake(0, 0, CGRectGetWidth(_webView.frame), self.progressHeight);
    _progressView.backgroundColor = [UIColor clearColor];
    [self addSubview:_progressView];
    [self bringSubviewToFront:_progressView];

    if (!self.progressLayer) {
        self.progressLayer = [CALayer layer];
        self.progressLayer.frame = CGRectMake(0, 0, self.showProgress ? 0.1 : 0, self.progressHeight);
        self.progressLayer.backgroundColor = self.progressColor.CGColor;
    }
    [_progressView.layer addSublayer:self.progressLayer];
}

- (CustomLongPressGestureRecognizer *)longPressGesture {
    __weak typeof (self) weakSelf = self;
    CustomLongPressGestureRecognizer *longPress = [[CustomLongPressGestureRecognizer alloc] initWithTarget:weakSelf action:nil];
    longPress.delegate = weakSelf; //记得在.h文件里加上委托
    longPress.minimumPressDuration = 0.4; //这里为什么要设置0.4，因为只要大于0.5就无效，大概是因为默认的跳出放大镜的手势的长按时间是0.5秒，
    //如果我们自定义的手势大于或小于0.5秒的话就来不及替换他的默认手势了，这只是猜测。但是最好大于0.2秒，因为有的pdf有一些书签跳转功能，这个值太小的话可能会使这些功能失效。
    return longPress;
}

- (void)clearLongPressGesture:(WKWebView *)webView {
    [webView.gestureRecognizers enumerateObjectsUsingBlock:^(__kindof UIGestureRecognizer * _Nonnull gesture, NSUInteger idx, BOOL * _Nonnull stop) {
        if ([gesture isKindOfClass:[CustomLongPressGestureRecognizer class]]) {
            gesture.delegate = nil;
            [webView removeGestureRecognizer:gesture];
        }
    }];
}

- (void)loadRequest:(NSURLRequest *)request {
    [super loadRequest:request];
    RCTExecuteOnMainQueue(^{
        [_webView loadRequest:request];
    });
}

- (void)loadCookieRequest:(NSURLRequest *)request {
    __weak typeof(self) weakSelf = self;
    if (!self.wkCookies) {
        [self loadRequest:request];
    } else {
        if (@available(iOS 11.0, *)) {
            __block NSInteger finishedCount = 0;
            NSInteger total = self.wkCookies.count;
            if (total == 0) {
                [self loadRequest:request];
            } else {
                for (NSHTTPCookie *cookie in self.wkCookies) {
                    [[WKWebsiteDataStore defaultDataStore].httpCookieStore setCookie:cookie completionHandler:^{
                        finishedCount++;
                        if (finishedCount >= total) {
                            [weakSelf loadRequest:request];
                        }
                    }];
                }
            }
        } else {
            // Fallback on earlier versions
            [self loadRequest:request];
        }
    }
}

- (void)goForward
{
    if([_webView canGoForward]){
        [_webView goForward];
    }
}

- (void)goBack
{
    [_webView goBack];
}

- (void)reload
{
    NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
    if (request.URL && !_webView.URL.absoluteString.length) {
        [self loadRequest:request];
    }
    else {
        [_webView reload];
    }
}

- (BOOL)appendHeaders:(NSDictionary<NSString *, NSString *> *)headers toRequest:(NSMutableURLRequest *)request {
    BOOL merged = NO;
    for (NSString *key in headers) {
        if (headers[key]) {
            if (![[request.allHTTPHeaderFields allKeys] containsObject:key]) {
                merged = YES;
                [request setValue:headers[key] forHTTPHeaderField:key];
            }
        }
    }

    return merged;
}

- (BOOL)mergeHeaders:(NSDictionary<NSString *, NSString *> *)headers toRequest:(NSMutableURLRequest *)request {
    BOOL merged = NO;
    for (NSString *key in headers) {
        if (headers[key]) {
            if (![request.allHTTPHeaderFields[key] isEqualToString:headers[key]]) {
                merged = YES;
            }
            [request setValue:headers[key] forHTTPHeaderField:key];
        }
    }

    return merged;
}

- (void)setAssetsCacheEnable:(BOOL)assetsCacheEnable {
    if (_assetsCacheEnable != assetsCacheEnable) {
        _assetsCacheEnable = assetsCacheEnable;
        if (_useURLProtocolForWKWebview) {
            if (self.assetsCacheEnable) {
                [YuanXinURLProtocol startListeningNetWorking];
                // NSURLProtocol+WKWebVIew
                [NSURLProtocol wk_registerScheme:@"http"];
                [NSURLProtocol wk_registerScheme:@"https"];
            }
            [YuanXinURLProtocol setUseCache:assetsCacheEnable];
        }
    }
}

- (void)setUserName:(NSString *)userName {
    _userName = userName;
    [YuanXinURLProtocol setUserName:userName];
}

- (void)setPassword:(NSString *)password {
    _password = password;
    [YuanXinURLProtocol setPassword:password];
}

- (void)setUserAgent:(NSString *)userAgent {
    _userAgent = userAgent;
    if (userAgent) {
        [self setupAndLoad];
    }
}

- (void)setWebViewUA:(NSString *)customUA completeHandler:(void (^)(void))completeHandler {
    if (_webView == nil || customUA == nil) {
        if (completeHandler) {
            completeHandler();
        }
        return;
    }
    NSString *ua = customUA;
    self.fakeWKWebView = [CustomWKWebView new];
    if (@available(iOS 12.0, *)){
        //由于iOS12的UA改为异步，所以不管在js还是客户端第一次加载都获取不到，所以此时需要先设置好再去获取
        NSString *userAgent = [_webView valueForKey:@"applicationNameForUserAgent"];
        if (![userAgent containsString:ua]) {
            NSString *newUserAgent = [NSString stringWithFormat:@"%@%@",userAgent, ua];
            [_webView setValue:newUserAgent forKey:@"applicationNameForUserAgent"];
            NSLog(@"%@>>>%@>>>>>",userAgent,newUserAgent);
        }
    }
    __weak typeof(self) weakSelf = self;
    [self.fakeWKWebView evaluateJavaScript:@"navigator.userAgent" completionHandler:^(id _Nullable result, NSError * _Nullable error) {
        NSString *userAgent = result;
        
        if ([userAgent rangeOfString:ua].location == NSNotFound) {
            NSString *newUserAgent = [NSString stringWithFormat:@"%@%@",userAgent, ua];
            NSLog(@"%@=>=>=>%@=>=>=>",userAgent,newUserAgent);
            //不添加以下代码则只是在本地更改UA，网页并未同步更改
            if (@available(iOS 9.0, *)) {
                [_webView setCustomUserAgent:newUserAgent];
            } else {
                [_webView setValue:newUserAgent forKey:@"applicationNameForUserAgent"];
            }
        }
        weakSelf.fakeWKWebView = nil;
        if (completeHandler) {
            completeHandler();
        }
    }]; //加载请求必须同步在设置UA的后面
}

- (void)setSource:(NSDictionary *)source
{
    if (![_source isEqualToDictionary:source]) {
        _source = [source copy];
        [self setupAndLoad];
    }
}

- (void)setupAndLoad {
    //TODO:目前用重置 webview(包括重新注入js) 的方式解决某些页面可能无法刷新（重新设置source）的问题
    [self setupWebView];
    __weak typeof(self) weakSelf = self;
    if (self.userAgent) {
        [self setWebViewUA:self.userAgent completeHandler:^{
            RCTExecuteOnMainQueue(^{
                [weakSelf loadSource];
            });
        }];
    } else {
        [self loadSource];
    }
}

- (void)loadSource {
    /* 和 YuanXinURLProtocol 合并了
     // 是否开启缓存
     if (self.assetsCacheEnable) {
     //使用方法，在开启webview的时候开启监听，，销毁weibview的时候取消监听，否则监听还在继续。将会监听所有的网络请求
     [JWCacheURLProtocol startListeningNetWorking];
     // NSURLProtocol+WKWebVIew
     [NSURLProtocol wk_registerScheme:@"http"];
     [NSURLProtocol wk_registerScheme:@"https"];
     } else {
     [JWCacheURLProtocol cancelListeningNetWorking];//在不需要用到webview的时候及时的取消监听
     // NSURLProtocol+WKWebVIew
     [NSURLProtocol wk_unregisterScheme:@"http"];
     [NSURLProtocol wk_unregisterScheme:@"https"];
     }
     */

    NSDictionary *customHeaders = [RCTConvert NSDictionary:self.source[@"headers"]];
    if (customHeaders && customHeaders.count > 0) {
        if (_useURLProtocolForWKWebview) {
            // NSURLProtocol+WKWebVIew
            [NSURLProtocol wk_registerScheme:@"http"];
            [NSURLProtocol wk_registerScheme:@"https"];
            //
            [YuanXinURLProtocol startListeningNetWorking];
            [YuanXinURLProtocol setRequestHeaders:customHeaders];
        }
    }

    // Check for a static html source first
    NSString *html = [RCTConvert NSString:self.source[@"html"]];
    if (html) {
        NSURL *baseURL = [RCTConvert NSURL:self.source[@"baseUrl"]];
        if (!baseURL) {
            baseURL = [NSURL URLWithString:@"about:blank"];
        }
        [_webView loadHTMLString:html baseURL:baseURL];
        return;
    }

    NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
    //_sourceHeaders = request.allHTTPHeaderFields;
    _sourceHeaders = customHeaders;

    // Because of the way React works, as pages redirect, we actually end up
    // passing the redirect urls back here, so we ignore them if trying to load
    // the same url. We'll expose a call to 'reload' to allow a user to load
    // the existing page.
    if ([request.URL isEqual:_webView.URL]) {
        return;
    }
    if (!request.URL) {
        // Clear the webview
        [_webView loadHTMLString:@"" baseURL:nil];
        return;
    }
    __weak typeof(self) weakSelf = self;
    RCTExecuteOnMainQueue(^{
        [weakSelf loadCookieRequest:request];
    });
}

- (void)setProgressStyle:(NSDictionary *)progressStyle
{
    if (![_progressStyle isEqualToDictionary:progressStyle]) {
        _progressStyle = [progressStyle copy];
        //UIColor *progressColor = [RCTConvert UIColor:progressStyle[@"color"]];
        UIColor *progressColor = [[self class] colorWithString:[RCTConvert NSString:progressStyle[@"color"]]];
        if (progressColor) {
            self.progressColor = progressColor;
            self.progressLayer.backgroundColor = self.progressColor.CGColor;
        }
        CGFloat progressHeight = [RCTConvert CGFloat:progressStyle[@"height"]];
        if (progressHeight > 0) {
            self.progressHeight = progressHeight;
        }
    }
}

- (void)layoutSubviews
{
    [super layoutSubviews];
    _webView.frame = self.bounds;
}

- (void)setContentInset:(UIEdgeInsets)contentInset
{
    _contentInset = contentInset;
    [RCTView autoAdjustInsetsForView:self
                      withScrollView:_webView.scrollView
                        updateOffset:NO];
}

- (void)setBackgroundColor:(UIColor *)backgroundColor
{
    CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
    self.opaque = _webView.opaque = (alpha == 1.0);
    _webView.backgroundColor = backgroundColor;
}

- (UIColor *)backgroundColor
{
    return _webView.backgroundColor;
}

- (void)setInjectedJavaScript:(NSString *)injectedJavaScript {
    _injectedJavaScript = injectedJavaScript;
    [self setupAndLoad];
}

- (void)setNoTouchCallout:(BOOL)noTouchCallout {
    _noTouchCallout = noTouchCallout;
    _webView.noTouchCallout = noTouchCallout;
}

- (NSMutableDictionary<NSString *, id> *)baseEvent
{
    NSMutableDictionary<NSString *, id> *event = [[NSMutableDictionary alloc] initWithDictionary:@{
                                                                                                   @"url": _webView.URL.absoluteString ?: @"",
                                                                                                   @"loading" : @(_webView.loading),
                                                                                                   @"title": _webView.title,
                                                                                                   @"canGoBack": @(_webView.canGoBack),
                                                                                                   @"canGoForward" : @(_webView.canGoForward),
                                                                                                   }];

    return event;
}

- (void)refreshContentInset
{
    [RCTView autoAdjustInsetsForView:self
                      withScrollView:_webView.scrollView
                        updateOffset:YES];
}

/**
 *  React 将消息推送到webView
 *
 *  @param value 传入参数
 */
- (void)sendToBridge:(NSString *)value{
//    NSString *format = NSStringMultiline(
//        (function(){
//            if (WebViewBridge && WebViewBridge.pushMessage) {
//                WebViewBridge.pushMessage(%@);
//            }
//        }());
//    );
    NSString *format = NSStringMultiline(
        if (WebViewBridge && WebViewBridge.pushMessage) {
            WebViewBridge.pushMessage(%@);
        }
    );
    NSString *command = [NSString stringWithFormat: format, RCTJSONStringify(value, NULL)];

    [_webView evaluateJavaScript:command completionHandler:^(id result, NSError * _Nullable error) {
        //
        if (error) {
            NSLog(@"evaluateJavaScript error:%@", error);
        }
    }];
}

- (void)evaluateScript:(NSString *)script resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject{

    NSString *jsWrapper = [NSString stringWithFormat:@"\
                           setTimeout(function(){ \
                           %@ }, 0)", script];
    __block BOOL finished = NO;
    __block RCTPromiseResolveBlock _resolve = resolve;
    __block RCTPromiseRejectBlock _reject = reject;
    [_webView evaluateJavaScript:jsWrapper completionHandler:^(id result, NSError *error) {
        if (error == nil) {
            if (_resolve) {
                _resolve(result);
            }
            /*
             if (result != nil) {
             //resultString = [NSString stringWithFormat:@"%@", result];
             }
             */
        } else {
            NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);

            NSString *description = error.localizedDescription;
            NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: description };
            NSError *error = [NSError errorWithDomain:@"WKWebViewManagerErrorDomain"
                                                 code:-57
                                             userInfo:userInfo];
            if (_reject) {
                _reject(@"-57", description, error);
            }

        }
        finished =YES;
    }];
}

/*
 - (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script {
 __block NSString *resultString = nil;
 __block BOOL finished = NO;

 [self evaluateJavaScript:script completionHandler:^(id result, NSError *error) {
 if (error == nil) {
 if (result != nil) {
 resultString = [NSString stringWithFormat:@"%@", result];
 }
 } else {
 NSLog(@"evaluateJavaScript error : %@", error.localizedDescription);
 }
 finished = YES;
 }];

 while (!finished)
 {
 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
 }

 return resultString;
 }
 */

- (void)addUserScriptAjaxHandlerToUserContentController:(WKUserContentController *) userContentController{
    //NSString *jsHandler = [NSString stringWithContentsOfURL:[[NSBundle mainBundle]URLForResource:@"ajaxHandler" withExtension:@"js"] encoding:NSUTF8StringEncoding error:NULL];
    NSString *jsHandler = [self ajaxHandler];
    WKUserScript *ajaxHandler = [[WKUserScript alloc]initWithSource:jsHandler injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
    [userContentController addScriptMessageHandler:[[WeakScriptMessageDelegate alloc] initWithDelegate:self] name:@"callbackHandler"];
    [userContentController addUserScript:ajaxHandler];
}

- (void)addUserScriptInjectedDefaultToUserContentController:(WKUserContentController *) userContentController{
    NSString *script = NSStringMultiline((function (params) {
        function guid () {
            function s4() {
                return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
            };

            return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
        };

        //alert('outer:' + params.webViewBridge);
        if (!params.webViewBridge) {
            //alert('inner:' + params.webViewBridge);
            params.webViewBridge = {
                send: function (data) {
                    WebViewBridge.send(JSON.stringify(data));
                },
                call: function (fnName, data) {
                    var msgObj = {
                    fnName: fnName,
                    data: data || {},
                        id: guid(),
                    };
                    var msg = JSON.stringify(msgObj);
                    return new Promise(function (resolve, reject) {
                        WebViewBridge.promises[msgObj.id] = { resolve: resolve, reject: reject };
                        WebViewBridge.send(msg);
                    });
                }
            };
            //alert('inner~:' + params.webViewBridge.send);
        }
    })(window););
    WKUserScript *userScript = [[WKUserScript alloc]initWithSource:script injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];
    [userContentController addUserScript:userScript];
}

- (NSString *)bridgeScript{
    return NSStringMultiline((function (exports) {

        function callFunc(func, message) {
            //alert("WebViewBridge:"+WebViewBridge);
            //alert(func);
            //alert(message);
            if ('function' === typeof func) {
                func(message);
            }
        }

        exports.WebViewBridge = {
            pushMessage: function (message) {
                setTimeout(function () {
                    callFunc(WebViewBridge.onMessage, message);
                }, 0); //this magic number is just a random small value. I don't like 0.
            },
            send: function(message) {
                if ('string' !== typeof message) {

                } else {
                    postMessage('YuanXin', message)
                }
            },
        };

        function guid () {
            function s4() {
                return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
            };

            return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
        };

        if(WebViewBridge.prototype) {
            WebViewBridge.prototype.onMessage = null;
            WebViewBridge.prototype.onError = null;
        }

        function postMessage(name, params) {
            window.webkit.messageHandlers[name].postMessage(params);
        }


        function genUniqueId(lookup) {
            var id = genId();

            while (lookup[id]) {
                id = genId()
            }

            return id;
        }

        function genId() {
            return Math.random().toString().slice(2);
        }

        function base64ToData(string) {
            var binaryString = window.atob(string);
            var len = binaryString.length;
            var bytes = new Uint8Array(len);

            for (var i = 0; i < len; i++) {
                bytes[i] = binaryString.charCodeAt(i);
            }

            return bytes.buffer;
        }


        function dataToBase64(data) {
            var binary = false;

            if (data instanceof ArrayBuffer) {
                data = String.fromCharCode.apply(null, new Uint8Array(data));
                binary = true;
            } else if (isTypedArray(data)) {
                data = String.fromCharCode.apply(null, data);
                binary = true;
            }

            return {
                data: window.btoa(data),
                binary: binary
            };
        }

        function isTypedArray(obj) {
            return obj && obj.buffer;
        }

    })(window); );
}

- (NSString *)ajaxHandler {
    //Every time an Ajax call is being invoked the listener will recognize it and  will call the native app with the request details
    /*
    $( document ).ajaxSend(function( event, request, settings )  {
        callNativeApp (settings.data);
    });

    function callNativeApp (data) {
        try {
            webkit.messageHandlers.callbackHandler.postMessage(data);
        }
        catch(err) {
            console.log('The native context does not exist yet');
        }
    }
    */

//    return NSStringMultiline(
//
//         $( document ).ajaxSend(function( event, request, settings )  {
//             callNativeApp (settings.data);
//         })
//
//         function callNativeApp (data) {
//             try {
//                 webkit.messageHandlers.callbackHandler.postMessage(data);
//             }
//             catch(err) {
//                 console.log('The native context does not exist yet');
//             }
//         }
//    );

    /*
    var open = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function() {
        this.addEventListener("load", function() {
            var message = {"status" : this.status, "responseURL" : this.responseURL}
            webkit.messageHandlers.handler.postMessage(message);
        });
        open.apply(this, arguments);
    };
    */

//    return NSStringMultiline(
//         var open = XMLHttpRequest.prototype.open;
//         XMLHttpRequest.prototype.open = function() {
//             this.addEventListener("load", function() {
//                 var message = {"status" : this.status, "responseURL" : this.responseURL}
//                 webkit.messageHandlers.callbackHandler.postMessage(message);
//             });
//             open.apply(this, arguments);
//         };
//    );

    /*
    var _proxysend = window.XMLHttpRequest.prototype.send;
    window.XMLHttpRequest.prototype.send = function() {
        // Saving body
        if (arguments[0]) {
            if (window.body_dictionary == undefined) {
                window.body_dictionary = new Object();
            }
            window.body_dictionary[this._url.hashCode()] = arguments[0].toString();
        }
        return _proxysend.apply(this, arguments);
    }
    */

//    return NSStringMultiline(
//         var _proxysend = window.XMLHttpRequest.prototype.send;
//         window.XMLHttpRequest.prototype.send = function() {
//             // Saving body
//             if (arguments[0]) {
//                 if (window.body_dictionary == undefined) {
//                     window.body_dictionary = new Object();
//                 }
//                 window.body_dictionary[this._url.hashCode()] = arguments[0].toString();
//                 webkit.messageHandlers.callbackHandler.postMessage(window.body_dictionary);
//             }
//             return _proxysend.apply(this, arguments);
//         }
//    );

    return NSStringMultiline(
        var _proxyopen = XMLHttpRequest.prototype.open;
        XMLHttpRequest.prototype.open = function() {
            //alert(JSON.stringify(arguments));
            // 取到的 this 为空对象 {}，无奈取参数 arguments，这样存在风险的。。
            // 而且URL很可能是相对路径。。
            if (arguments[1]) {
                window.current_open_url = arguments[1].toString();
            }
            _proxyopen.apply(this, arguments);
        };

        var _proxysend = window.XMLHttpRequest.prototype.send;
        window.XMLHttpRequest.prototype.send = function() {
            //alert(JSON.stringify(arguments));
            // Saving body
            if (arguments[0]) {
                if (window.body_dictionary == undefined) {
                    window.body_dictionary = new Object();
                }
                if (window.current_open_url) {
                    window.body_dictionary[window.current_open_url] = arguments[0].toString();
                    webkit.messageHandlers.callbackHandler.postMessage(window.body_dictionary);
                }
            }
            return _proxysend.apply(this, arguments);
        }
    );
}

- (NSString *)injectNoneSelectScript {
    //禁止长按弹出 UIMenuController 相关
    //禁止选择 css 配置相关
    NSString*css = @"body{-webkit-user-select:none;-webkit-user-drag:none;}";
    //css 选中样式取消
    NSMutableString*javascript = [NSMutableString string];
    [javascript appendString:@"var style = document.createElement('style');"];
    [javascript appendString:@"style.type = 'text/css';"];
    [javascript appendFormat:@"var cssContent = document.createTextNode('%@');", css];
    [javascript appendString:@"style.appendChild(cssContent);"];
    [javascript appendString:@"document.body.appendChild(style);"];
    [javascript appendString:@"document.documentElement.style.webkitUserSelect='none';"];//禁止选择
    [javascript appendString:@"document.documentElement.style.webkitTouchCallout='none';"];//禁止长按

    return javascript;
}

- (void)disableUITextTapRecognizer:(WKWebView *)webView {
    for (UIView *contentView in webView.scrollView.subviews) {
        [contentView.gestureRecognizers enumerateObjectsUsingBlock:^(__kindof UIGestureRecognizer * _Nonnull gesture, NSUInteger idx, BOOL * _Nonnull stop) {
            NSString *gestureName = NSStringFromClass([gesture class]);
            // if (![gestureName isEqualToString:@"UIWebTouchEventsGestureRecognizer"]) {
            if ([gestureName containsString:@"LongPress"]) {
                //
                [gesture setEnabled:NO];
            }
        }];
    }
}

/**
 清理缓存
 */
- (void)clearWbCache {
    [YuanXinWebView clearWbCache];
}

#pragma mark - Notification
- (void)keyboardWillShown:(NSNotification*)aNotification

{
    if (self.iosKeyboardDisableAutoScroll) {
        _oldScrollDelegate = _webView.scrollView.delegate;
        _oldOffset = _webView.scrollView.contentOffset;
        _webView.scrollView.delegate = self;
    }
}

//- (void)keyboardWasShown:(NSNotification*)aNotification
//{
//    if (self.iosKeyboardDisableAutoScroll) {
//        _webView.scrollView.delegate = _oldScrollDelegate;
//    }
//}

- (void)keyboardWillHide:(NSNotification*)aNotification
{
    if (self.iosKeyboardDisableAutoScroll) {
        _webView.scrollView.delegate = _oldScrollDelegate;
    }
}

- (void)handleDeviceOrientationDidChange {
    
    if (!self.videoFullScreen) {
        
        return;
    }
    
    UIDevice *device = [UIDevice currentDevice];
    switch (device.orientation) {
        case UIDeviceOrientationFaceUp:
            [self endFullScreen];
            NSLog(@"屏幕朝上平躺");
            break;
            
        case UIDeviceOrientationFaceDown:
            [self endFullScreen];
            NSLog(@"屏幕朝下平躺");
            break;
            
        case UIDeviceOrientationUnknown:
            [self endFullScreen];
            NSLog(@"未知方向");
            break;
            
        case UIDeviceOrientationLandscapeLeft:
            [self startFullScreenRight];
            NSLog(@"屏幕向左横置");
            break;
            
        case UIDeviceOrientationLandscapeRight:
            [self startFullScreenLeft];
            NSLog(@"屏幕向右橫置");
            break;
            
        case UIDeviceOrientationPortrait:
            [self endFullScreen];
            NSLog(@"屏幕直立");
            break;
            
        case UIDeviceOrientationPortraitUpsideDown:
            [self endFullScreenUpsideDown];
            NSLog(@"屏幕直立，上下顛倒");
            break;
            
        default:
            [self endFullScreen];
            NSLog(@"无法辨识");
            break;
    }
}

- (void)startFullScreenRight {
    
    NSLog(@"进入全屏");
    UIApplication *application = [UIApplication sharedApplication];
    [application setStatusBarOrientation: UIInterfaceOrientationLandscapeRight];
    application.keyWindow.transform = CGAffineTransformMakeRotation(M_PI / 2);
    application.keyWindow.bounds = CGRectMake(0, 0, kSCREEN_WIDTH, kSCREEN_HEIGHT);
}

- (void)startFullScreenLeft {
    
    NSLog(@"进入全屏");
    UIApplication *application = [UIApplication sharedApplication];
    [application setStatusBarOrientation: UIInterfaceOrientationLandscapeRight];
    application.keyWindow.transform = CGAffineTransformMakeRotation(3 * M_PI / 2);
    application.keyWindow.bounds = CGRectMake(0, 0, kSCREEN_WIDTH, kSCREEN_HEIGHT);
}

- (void)endFullScreen {
    NSLog(@"退出全屏XXXX");
    UIApplication *application=[UIApplication sharedApplication];
    [application setStatusBarOrientation: UIInterfaceOrientationPortrait];
    application.keyWindow.bounds = CGRectMake(0, 0, kSCREEN_WIDTH, kSCREEN_HEIGHT);
    application.keyWindow.transform = CGAffineTransformMakeRotation(M_PI * 2);
    [application setStatusBarHidden:NO];
}

- (void)endFullScreenUpsideDown {
    
    NSLog(@"退出全屏XXXX");
    UIApplication *application=[UIApplication sharedApplication];
    [application setStatusBarOrientation: UIInterfaceOrientationPortrait];
    application.keyWindow.bounds = CGRectMake(0, 0, kSCREEN_WIDTH, kSCREEN_HEIGHT);
    application.keyWindow.transform = CGAffineTransformMakeRotation(M_PI);
    [application setStatusBarHidden:NO];
}

- (void)videoBeginFullScreen {
    
    self.videoFullScreen = YES;
}

- (void)videoStopFullScreen {
    
    self.videoFullScreen = NO;
    UIApplication *application=[UIApplication sharedApplication];
    [application setStatusBarOrientation: UIInterfaceOrientationPortrait];
    [application setStatusBarHidden:NO];
}

//强制转屏
- (void)setOrientation:(NSString *)orientation {
    if ([orientation.lowercaseString containsString:@"portrait"]) {
        [self setOrientationPortrait];
    } else {
        [self setOrientationLandscape:orientation];
    }
}

// 强制横屏
- (void)setOrientationLandscape:(NSString *)landscape {
    if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(setAllowRotationString:)]) {
        //允许转成横屏
        [[UIApplication sharedApplication].delegate performSelector:@selector(setAllowRotationString:) withObject:@"yes"];
    }
    //调用横屏代码
    if ([landscape.lowercaseString containsString:@"right"]) {
        [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
    } else {
        [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeLeft];
    }
}

- (void)setOrientationPortrait {
    if ([[UIApplication sharedApplication].delegate respondsToSelector:@selector(setAllowRotationString:)]) {
        //关闭横屏仅允许竖屏
        [[UIApplication sharedApplication].delegate performSelector:@selector(setAllowRotationString:) withObject:@"no"];
    }
    //切换到竖屏
    [UIDevice switchNewOrientation:UIInterfaceOrientationPortrait];
}

#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (self.iosKeyboardDisableAutoScroll) {
        scrollView.contentOffset = _oldOffset;
    }
}

#pragma mark - key-value-observe

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context
{
    __weak typeof(self) weakSelf = self;
    if ([keyPath isEqualToString:@"estimatedProgress"]) {
        if (_onProgress) {
            _onProgress(@{@"progress": [change objectForKey:NSKeyValueChangeNewKey]});
        }
        if (self.showProgress) {
            self.progressLayer.opacity = 1;
            CGFloat widthPersent = MAX([change[NSKeyValueChangeNewKey] floatValue], 0.1);
            self.progressLayer.frame = CGRectMake(0, 0, _webView.bounds.size.width * widthPersent, self.progressHeight);
            if ([change[NSKeyValueChangeNewKey] floatValue] == 1) {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    weakSelf.progressLayer.opacity = 0;
                });
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(.8 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    weakSelf.progressLayer.frame = CGRectMake(0, 0, 0, weakSelf.progressHeight);
                });
            }
        }
    }else{
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

#pragma mark -- WKNavigationDelegate methods

- (void)webView:(__unused WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
    NSMutableURLRequest *mRequest = [navigationAction.request mutableCopy];
    NSURL* url = mRequest.URL;
    NSString* scheme = url.scheme;

    //BOOL isJSNavigation = [scheme isEqualToString:RCTJSNavigationScheme]; // RCTJSNavigationScheme 已经没有了，先去掉

    /* 换用 NSURLProtocol 全局监听
    if (!isJSNavigation && [self appendHeaders:_sourceHeaders toRequest:mRequest]) {
        RCTExecuteOnMainQueue(^{
            [self loadCookieRequest:mRequest];
        });
        return decisionHandler(WKNavigationActionPolicyCancel);
    }
     */

    // skip this for the JS Navigation handler
    if (_onShouldStartLoadWithRequest) {
        NSMutableDictionary<NSString *, id> *event = [self baseEvent];
        [event addEntriesFromDictionary: @{
                                           @"url": (mRequest.URL).absoluteString,
                                           @"navigationType": @(navigationAction.navigationType)
                                           }];
        if (![self.delegate webView:self
          shouldStartLoadForRequest:event
                       withCallback:_onShouldStartLoadWithRequest]) {
            return decisionHandler(WKNavigationActionPolicyCancel);
        }
    }

    // 转交给一个 NSURLSessionDataTask 处理请求(主要是为了处理认证)//WKWebView自己就有验证的代理不需要借助 NSURLSession
//    if (!_authed) {
//        _authed = NO;
//        NSURLSessionDataTask* sessionTask = [self.sessionClient dataTaskWithRequest:request];
//        [sessionTask resume];
//        return decisionHandler(WKNavigationActionPolicyCancel);
//    }

    if (_onLoadingStart) {
        // We have this check to filter out iframe requests and whatnot
        BOOL isTopFrame = [url isEqual:mRequest.mainDocumentURL];
        if (isTopFrame) {
            NSMutableDictionary<NSString *, id> *event = [self baseEvent];
            [event addEntriesFromDictionary: @{
                                               @"url": url.absoluteString,
                                               @"navigationType": @(navigationAction.navigationType)
                                               }];
            _onLoadingStart(event);
        }
    }
    
    if (navigationAction.targetFrame && ([scheme isEqualToString:@"http"] || [scheme isEqualToString:@"https"])) {
        decisionHandler(WKNavigationActionPolicyAllow);
    }
    else {
        if (![scheme isEqualToString:@"about"]) {
            [[UIApplication sharedApplication] openURL:url];
        }
        decisionHandler(WKNavigationActionPolicyAllow);
    }
}

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
    decisionHandler(WKNavigationResponsePolicyAllow);
}

- (void)webView:(WKWebView *)webView didReceiveServerRedirectForProvisionalNavigation:(null_unspecified WKNavigation *)navigation { // 似乎不会调用
    //NSLog(@"didReceiveServerRedirectForProvisionalNavigation");
}

- (void)webView:(__unused WKWebView *)webView didFailProvisionalNavigation:(__unused WKNavigation *)navigation withError:(NSError *)error
{
    if (_onLoadingError) {
        if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
            // NSURLErrorCancelled is reported when a page has a redirect OR if you load
            // a new URL in the WebView before the previous one came back. We can just
            // ignore these since they aren't real errors.
            // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os
            return;
        }

        NSMutableDictionary<NSString *, id> *event = [self baseEvent];
        [event addEntriesFromDictionary:@{
                                          @"domain": error.domain,
                                          @"code": @(error.code),
                                          @"description": error.localizedDescription,
                                          }];
        NSString *failingUrl = error.userInfo[NSURLErrorFailingURLStringErrorKey];
        failingUrl = failingUrl ?: webView.URL.absoluteString;
        if (failingUrl) {
            [event setObject:error.userInfo[NSURLErrorFailingURLStringErrorKey] ?: @"" forKey:@"failingUrl"];
        }
        _onLoadingError(event);
    }
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(__unused WKNavigation *)navigation
{
    __weak typeof(self) weakSelf = self;
    if (self.noTouchCallout) { //
        [self disableUITextTapRecognizer:webView]; // 彻底屏蔽可能造成弹出选项框的可能(为兼容iOS 10)
        [self clearLongPressGesture:webView];
        [webView addGestureRecognizer:[self longPressGesture]];
    } else {
        [self clearLongPressGesture:webView];
    }
    /*
    // 注入传进来的 js
    if (_injectedJavaScript != nil) {
        [webView evaluateJavaScript:_injectedJavaScript completionHandler:^(id result, NSError *error) {
            NSMutableDictionary<NSString *, id> *event = [self baseEvent];
            event[@"jsEvaluationValue"] = [NSString stringWithFormat:@"%@", result];
            if (weakSelf.onLoadingFinish) {
                weakSelf.onLoadingFinish(event);
            }
        }];
    }
    // we only need the final 'finishLoad' call so only fire the event when we're actually done loading.
    else */if (_onLoadingFinish && !webView.loading && ![webView.URL.absoluteString isEqualToString:@"about:blank"]) {
        _onLoadingFinish([self baseEvent]);
    }
}

- (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler {
    // 判断服务器采用的验证方法
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM])
    {
        if ([challenge previousFailureCount] == 0) {
            /* SET YOUR credentials, i'm just hard coding them in, tweak as necessary */
            if (self.userName && self.password) {
                NSURLCredential *credential = [NSURLCredential credentialWithUser:self.userName password:self.password persistence:NSURLCredentialPersistencePermanent];
                completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
            } else {
                completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
            }
        } else {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }
    } else if (challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust) {
        // 如果没有错误的情况下 创建一个凭证，并使用证书
        if (challenge.previousFailureCount == 0) {
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
        }else {
            // 验证失败，取消本次验证
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }
    }else {
        completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
    }
}

- (void)webViewWebContentProcessDidTerminate:(WKWebView *)webView {
    // 当 WKWebView 总体内存占用过大，页面即将白屏的时候，系统会调用这个回调函数，
    // 我们在该函数里执行[webView reload](这个时候 webView.URL 取值尚不为 nil）解决白屏问题。
    // 在一些高内存消耗的页面可能会频繁刷新当前页面，H5侧也要做相应的适配操作。
    if (++_reloadCount > MAX_RELOAD) {
        _reloadCount = 0; // 重置
    } else {
        [webView reload];
    }
}

#pragma mark NSURLSessionDataDelegate

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {

    /** THIS IS WHERE YOU SET MAKE THE NEW REQUEST TO UIWebView, which will use the new saved auth info **/

    NSURLRequest *urlRequest = dataTask.originalRequest;

    if (_webView) {
        //尝试在每次请求wkWebview前清理缓存
        //[self clearWbCache];
        [_webView loadRequest:urlRequest];
    }

    completionHandler(NSURLSessionResponseCancel);
}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data {
    //NSLog(@"data = %@", data);
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
didCompleteWithError:(nullable NSError *)error {
    //
    _authed = YES;
    if (_sessionClient) {
        [_sessionClient finishTasksAndInvalidate];
        _sessionClient = nil;
    }

    if(!error) {
        //NSLog(@"task did complete, response status code -> %ld", (long)((NSHTTPURLResponse *)(task.response)).statusCode);
    } else if (error.code != NSURLErrorCancelled) {
        //
        NSURLRequest *urlRequest = task.originalRequest;

        if (_webView) {
            [_webView loadRequest:urlRequest];
        }
    }
}

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler{
    //如果是http代理，其它代理方法类似
    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodNTLM])
    {
        if ([challenge previousFailureCount] == 0) {
            /* SET YOUR credentials, i'm just hard coding them in, tweak as necessary */
            NSURLCredential *credential = [NSURLCredential credentialWithUser:self.userName password:self.password persistence:NSURLCredentialPersistencePermanent];
            completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
        } else {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }
    } else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]){
        if (challenge.previousFailureCount == 0) {
            NSURLCredential *credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
            completionHandler(NSURLSessionAuthChallengeUseCredential, credential);
        }else {
            completionHandler(NSURLSessionAuthChallengeCancelAuthenticationChallenge, nil);
        }
    } else {
        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling,nil);
    }
}

#pragma mark -- WKUIDelegate methods

- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler {

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }];
    [alertController addAction:okAction];

    UIViewController *keyRootController =  [RCTKeyWindow() rootViewController];
    [keyRootController presentViewController:alertController animated:YES completion:^(){}];
}

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(nonnull NSString *)message initiatedByFrame:(nonnull WKFrameInfo *)frame completionHandler:(nonnull void (^)(BOOL))completionHandler{

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提醒" message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        if (completionHandler) {
            completionHandler(YES);
        }
    }];
    [alertController addAction:okAction];

    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        if (completionHandler) {
            completionHandler(NO);
        }
    }];
    [alertController addAction:cancelAction];

    UIViewController *keyRootController =  [RCTKeyWindow() rootViewController];
    [keyRootController presentViewController:alertController animated:YES completion:^(){}];
}

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(nullable NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * __nullable result))completionHandler{

    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提醒" message:defaultText preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField *textField){
        // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(alertTextFieldDidChange:) name:UITextFieldTextDidChangeNotification object:textField];
    }];
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        NSString  *text = alertController.textFields[0].text;
        if (completionHandler) {
            completionHandler(text);
        }
    }];
    [alertController addAction:okAction];

    UIViewController *keyRootController =  [RCTKeyWindow() rootViewController];
    [keyRootController presentViewController:alertController animated:YES completion:^(){}];
}

- (nullable WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures {
    if (!navigationAction.targetFrame.isMainFrame)
    {
        [webView loadRequest:navigationAction.request];
    }
    return nil;
}

- (void)webViewDidClose:(WKWebView *)webView{

}

#pragma mark -- WKScriptMessageHandler methods
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{

    if ([message.name isEqualToString:@"YuanXin"]){
        id parames = message.body;
        //NSLog(@"userContentController parames:%@",parames);
        [self sendBridgeMessage:@"messages" body:parames];
        // 强制转屏
        if ([parames isKindOfClass:NSString.class]) {
            NSData *jsonData = [parames dataUsingEncoding:NSUTF8StringEncoding];
            NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
            if (dict && [dict[@"type"] isEqualToString:@"setOrientation"]) {
                [self setOrientation:dict[@"value"]];
            }
        }
    }

    if ([message.name isEqualToString:@"callbackHandler"]){
        id parames = message.body;
        if ([parames isKindOfClass:NSDictionary.class]) {
            if (_useURLProtocolForWKWebview) {
                [YuanXinURLProtocol setPostRequestBodies:parames];
            }
        }
    }

    NSLog(@"ReceiveScriptMessage %@",message.name);
}

#pragma mark - GestureRecognizerDelegate

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
    if ([otherGestureRecognizer isKindOfClass:[UITapGestureRecognizer class]]) {
        UITapGestureRecognizer *gesture = (UITapGestureRecognizer *)otherGestureRecognizer;
        if (gesture.numberOfTapsRequired == 2) {
            [otherGestureRecognizer.view removeGestureRecognizer:otherGestureRecognizer];
        }
    }

    return NO; //这里一定要return NO,至于为什么大家去看看这个方法的文档吧。
    //还有就是这个委托在你长按的时候会被多次调用，大家可以用nslog输出gestureRecognizer和otherGestureRecognizer
    //看看都是些什么东西。

}

@end
